#include "GlobalDefs.h"
#include <stdio.h>
#include <memory>

#ifdef SUPPORT_H264
#include "H264DecSDK.h"
#endif

const REFERENCE_TIME FPS_30 = UNITS / 30;
const REFERENCE_TIME FPS_20 = UNITS / 20;
const REFERENCE_TIME FPS_10 = UNITS / 10;
const REFERENCE_TIME FPS_5  = UNITS / 5;
const REFERENCE_TIME FPS_4  = UNITS / 4;
const REFERENCE_TIME FPS_3  = UNITS / 3;
const REFERENCE_TIME FPS_2  = UNITS / 2;
const REFERENCE_TIME FPS_1  = UNITS / 1;

const REFERENCE_TIME rtDefaultFrameLength = FPS_10;

#define DATASIZE 4096

typedef	struct {
	unsigned long channelNo;
	unsigned long videoLen;
	unsigned long timeTick;
	unsigned short audioLen;
	unsigned char bIFrame;
	unsigned char bVol;
} BtlFrameHeader;

/**********************************************
 *
 *  Class declarations
 *
 **********************************************/
class H264SourcePin : public CSourceStream
{
protected:
	HWND m_videoWnd;

    int m_iFrameNumber;
    REFERENCE_TIME m_rtFrameLength;

    int m_iImageHeight;                 // The current image height
    int m_iImageWidth;                  // And current image width
    int m_iRepeatTime;                  // Time in msec between frames
    int m_nCurrentBitDepth;             // Screen bit depth

    CMediaType m_MediaType;
    CCritSec m_cSharedState;            // Protects our internal state
    CImageDisplay m_Display;            // Figures out our media type for us

	DWORD m_lastUpdateTime;

	bool _getsize;
	bool _beginNewFrame;
	BtlFrameHeader _header;
	UINT _bytesForHeader;
	BYTE *_buffer;
	UINT _len;

	UINT _width, _height;				//demand video size by user

	BYTE *_videoBuffer1;
	BYTE *_videoBuffer2;
	BYTE *_currBuffer;
	UINT _videoBufferSize;

	DWORD _startTime;

	bool _isRawYuv;

	UINT _fps;

	DRAWCALLBACK _drawCallback;
	DECODECALLBACK _decodeCallback;
	PLAYBACKSESSION _playbackSession;
	long _drawCallbackParam;
	long _decodeCallbackParam;

	DWORD _lastFpsCountTime;
	DWORD _lastActualFps;
	DWORD _totalFrame;

#ifdef SUPPORT_H264
	H264DECSESSION m_session;
	bool Decode(int width, int height, BYTE *outbuf);
#endif

#ifdef NEED_DEINTERLACE
	UINT m_blockTotalY[44][36];	//[704/16][288/8]
	int DeinterlaceFrames(BYTE *destBuf, int destWidth, int destHeight);
#endif

	HRESULT DoBufferProcessingLoop(void);

	void ReleaseVideoBuffers();
	void AdjustVideoSize(int newWidth, int newHeight);

public:

    H264SourcePin(HRESULT *phr, CSource *pFilter, UINT fps, UINT width, UINT height, bool isRawYuv);
    ~H264SourcePin();

    // Override the version that offers exactly one media type
    HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest);
    HRESULT FillBuffer(IMediaSample *pSample);
    
    // Set the agreed media type and set up the necessary parameters
    HRESULT SetMediaType(const CMediaType *pMediaType);

    // Support multiple display formats
    HRESULT CheckMediaType(const CMediaType *pMediaType);
    HRESULT GetMediaType(int iPosition, CMediaType *pmt);

	void SetOutputWindow(HWND hwnd) { m_videoWnd = hwnd;}

    // Quality control
	// Not implemented because we aren't going in real time.
	// If the file-writing filter slows the graph down, we just do nothing, which means
	// wait until we're unblocked. No frames are ever dropped.
    STDMETHODIMP Notify(IBaseFilter *pSelf, Quality q)
    {
        return E_FAIL;
    }

	int GetActualVideoSize(int *width, int *height);
	int SetDrawCallback(PLAYBACKSESSION session, DRAWCALLBACK drawCallback, long param);
	int SetDecodeCallback(PLAYBACKSESSION session, DECODECALLBACK decodeCallback, long param);
	int ChangePlaybackFps(UINT fps);

	int GetSnapshotSize();
	int Snapshot(char *buf, int bufLen);

	int GetActualFps() 
	{ 	
		if (timeGetTime() - _lastFpsCountTime > 2 * BITRATE_COUNT_INTERVAL)
			return 0;
		else
			return _lastActualFps;
	}
};

#define MAX_FRAME_SIZE (720 * 576 * 3)

class H264SourceFilter : public CSource
{

private:
    H264SourcePin *m_pPin;
	char _filename[512];
	CCritSec _callbackLock;
	DataProviderCallBack _dataProvider;
	bool _isFileStream;
	FILE *_file;
	BYTE _buffer[MAX_FRAME_SIZE];
	int _len;
	PLAYBACKSESSION m_session;
	LPVOID _param;
	bool _isRawYuv;

	DWORD _lastBitrateCountTime;
	DWORD _lastBitrate;
	UINT _totalBit;

public:
	H264SourceFilter(IUnknown *pUnk, HRESULT *phr, UINT fps, UINT width, UINT height, bool isRawYuv);
	~H264SourceFilter();

	void SetSourceName(const char *filename);
	void SetDataProvider(PLAYBACKSESSION session, DataProviderCallBack dataProvider, LPVOID param);
	const char *GetSourceName() { return _filename;}
	CBasePin *GetOutputPin() { return m_pPin;}
	bool GetData(BYTE *&buffer, UINT &len);
	void SetOutputWindow(HWND hwnd) { if (m_pPin != NULL) m_pPin->SetOutputWindow(hwnd);}
	int GetBitrate();
	int GetActualFps() { if (m_pPin != NULL) return m_pPin->GetActualFps(); else return -1;}

	int GetActualVideoSize(int *width, int *height) { if (m_pPin != NULL) return m_pPin->GetActualVideoSize(width, height); return -1;}

	int SetDrawCallback(PLAYBACKSESSION session, DRAWCALLBACK drawCallback, long param) 
	{ 
		if (m_pPin != NULL) 
			return m_pPin->SetDrawCallback(session, drawCallback, param); 

		return -1;
	}

	int SetDecodeCallback(PLAYBACKSESSION session, DECODECALLBACK decodeCallback, long param) 
	{ 
		if (m_pPin != NULL) 
			return m_pPin->SetDecodeCallback(session, decodeCallback, param); 

		return -1;
	}

	int ChangePlaybackFps(UINT fps) { if (m_pPin != NULL) return m_pPin->ChangePlaybackFps(fps); return -1;}

	int GetSnapshotSize() { if (m_pPin != NULL) return m_pPin->GetSnapshotSize(); return -1;}
	int Snapshot(char *buf, int bufLen)  { if (m_pPin != NULL) return m_pPin->Snapshot(buf, bufLen); return -1;}
};


